package com.nsl.my_youlai.bbu2.service.impl;

import com.alibaba.excel.EasyExcel;
import com.nsl.my_youlai.bbu2.enums.ApparatusRoomCategoryEnum;
import com.nsl.my_youlai.bbu2.enums.NetGenerationEnum;
import com.nsl.my_youlai.bbu2.enums.VendorEnum2;
import com.nsl.my_youlai.bbu2.model.bo.ApparatusRoomBo;
import com.nsl.my_youlai.bbu2.model.bo.bbu.Bbu2;
import com.nsl.my_youlai.bbu2.model.bo.bbu.BbuBoard2;
import com.nsl.my_youlai.bbu2.model.bo.bbu.BbuSlot2;
import com.nsl.my_youlai.bbu2.model.vo.BbuBoardOutputVo;
import com.nsl.my_youlai.bbu2.model.vo.BbuOutputVo;
import com.nsl.my_youlai.bbu2.untils.UtilBbuReturn;
import com.nsl.my_youlai.mystar.easy_excel.model.ExcelReadResult;
import com.nsl.my_youlai.mystar.enums.NslEnumTransformException;
import com.nsl.my_youlai.mystar.gis.NslUtilGis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
public class BbuReturnServiceImpl {
    @Autowired
    BbuStatusExcelStringVoServiceImpl bbuVoService;

    @Autowired
    BbuServiceImpl bbuBoService;

    @Autowired
    ApparatusRoomServiceImpl roomService;

    public void returnBbu(String excelFilePath,String apparatusRoomSheetName,String bbuSheetName) throws NslEnumTransformException {
        ExcelReadResult readResult=bbuVoService.parseBbuStatusExcelSheet(excelFilePath,bbuSheetName);
        Map<Integer, Bbu2> bbuMap=bbuBoService.buildBbuMap(readResult.getRecordList());

        ExcelReadResult roomReadResult =roomService.readApparatusRoomVoStringFromExcel(excelFilePath,apparatusRoomSheetName);
        Map<String, ApparatusRoomBo> roomBoMap=roomService.voList2BoMap(roomReadResult.getRecordList());

        //整合前 bbu list
        outputBbuList(bbuMap,"before");

        List<ApparatusRoomBo> allRoomBoList=new ArrayList<>(roomBoMap.values());

        //link bbu and room
        for (Map.Entry<Integer, Bbu2> bbuEntry : bbuMap.entrySet()) {
            Bbu2 bbu=bbuEntry.getValue();
            String roomId=bbu.getApparatusRoomId();
            ApparatusRoomBo roomBo=roomBoMap.get(roomId);
            roomBo.getBbuList().add(bbu);
            bbu.setApparatusRoomBo(roomBo);
        }

        //锚点机房自我整合
        for (ApparatusRoomBo roomBo : roomBoMap.values()) {
            if (roomBo.isAnchorRoom()) {
                integrateWithInAnchorRoom(roomBo);
            }
        }

        //按照 核心 汇聚  接入 基站 的顺序 回局
        Map<String,ApparatusRoomBo> coreRoomMap= filterApparatusRoomByApparatusRoomCategoryEnum(roomBoMap,ApparatusRoomCategoryEnum.CORE_MACHINE_ROOM );
        Map<String,ApparatusRoomBo> aggregationMap= filterApparatusRoomByApparatusRoomCategoryEnum(roomBoMap,ApparatusRoomCategoryEnum.AGGREGATION_MACHINE_ROOM );
        Map<String,ApparatusRoomBo> accessRoom= filterApparatusRoomByApparatusRoomCategoryEnum(roomBoMap,ApparatusRoomCategoryEnum.ACCESS_MACHINE_ROOM );
        Map<String,ApparatusRoomBo> stationMap= filterApparatusRoomByApparatusRoomCategoryEnum(roomBoMap,ApparatusRoomCategoryEnum.COMMUNICATION_STATION_MACHINE_ROOM );

//        System.out.println(coreRoomMap.size()+aggregationMap.size()+accessRoom.size()+stationMap.size());

        List<String> orderedCoreRoomIdList=orderRoomBoByLonAndLat(coreRoomMap);
        List<String> aggregationRoomIdList=orderRoomBoByLonAndLat(aggregationMap);
        List<String> accessRoomIdList=orderRoomBoByLonAndLat(accessRoom);
        List<String> stationRoomIdList=orderRoomBoByLonAndLat(stationMap);

//        NslUtilPrinter.printHorizontal("core room");
        returnWholeAndIntegrate(orderedCoreRoomIdList,allRoomBoList,roomBoMap);
//        NslUtilPrinter.printHorizontal("aggregation room");
        returnWholeAndIntegrate(aggregationRoomIdList,allRoomBoList,roomBoMap);
//        NslUtilPrinter.printHorizontal("access room");
        returnWholeAndIntegrate(accessRoomIdList,allRoomBoList,roomBoMap);
//        NslUtilPrinter.printHorizontal("station room");
        returnWholeAndIntegrate(stationRoomIdList,allRoomBoList,roomBoMap);

        //输出结果1
        outputBbuList(bbuMap,"after");

        //输出结果2
        List<BbuBoardOutputVo> boardList=toBoardList(bbuMap);
        Random random=new Random();
        int randomInt=random.nextInt(90000);
        String outputFilePath="D:\\GS_Dir_Ddisk\\GS#i3#Coding\\Test\\Bbu_Return2\\整合后boardList_"+randomInt+".xlsx";

        EasyExcel.write(outputFilePath, BbuBoardOutputVo.class).sheet("output").doWrite(boardList);
    }

    //输出 BbuOutputVo List 表格
    public List<BbuOutputVo> outputBbuList(Map<Integer, Bbu2> bbuMap, String statusString){
        Random random=new Random();

        List<BbuOutputVo> voList=new ArrayList<>();
        for (Map.Entry<Integer, Bbu2> entry : bbuMap.entrySet()) {
            Bbu2 bbu2=entry.getValue();
            voList.add((toBbuOutputVo(bbu2)));
        }

        int randomInt=random.nextInt(90000);
        String outputFilePath="D:\\GS_Dir_Ddisk\\GS#i3#Coding\\Test\\Bbu_Return2\\"+statusString+"_bbuList_"+randomInt+".xlsx";
        EasyExcel.write(outputFilePath, BbuOutputVo.class).sheet("output").doWrite(voList);
        return voList;
    }

    public List<BbuBoardOutputVo> toBoardList(Map<Integer, Bbu2> bbuMap){
        List<BbuBoardOutputVo> boardList=new ArrayList<>();
        for (Map.Entry<Integer, Bbu2> entry : bbuMap.entrySet()) {
            Bbu2 bbu =entry.getValue();
            Map<Integer, BbuSlot2> slot2Map= bbu.getSlotMap();
            for (Map.Entry<Integer, BbuSlot2> slotEntry : slot2Map.entrySet()) {
                BbuSlot2 slot=slotEntry.getValue();
                for (BbuBoard2 bbuBoard2 : slot.getBbuBoardList()) {
                    BbuBoardOutputVo bbuBoardOutputVo=new BbuBoardOutputVo();
                    bbuBoardOutputVo.setSourceExcelIndex(bbuBoard2.getExcelRowIndex());
                    bbuBoardOutputVo.setNodeId(bbuBoard2.getNodeId());
                    bbuBoardOutputVo.setVendor(bbuBoard2.getVendor().getLabel());
                    bbuBoardOutputVo.setBbuModel(bbuBoard2.getBbuModelEnum().getLabel());
                    bbuBoardOutputVo.setApparatusRoomId(bbu.getApparatusRoomId());
                    bbuBoardOutputVo.setSlotIndex(slot.getSlotIndex());
                    bbuBoardOutputVo.setBoardType(bbuBoard2.getBoardTypeEnum().getLabel());
                    boardList.add(bbuBoardOutputVo);
                }
            }
        }
        return  boardList;
    }

    public BbuOutputVo toBbuOutputVo(Bbu2 bbu2){
        BbuOutputVo vo=new BbuOutputVo();
        vo.setBbuModel(bbu2.getBbuModelEnum().getLabel());
        vo.setNodeId(bbu2.getNodeId());
        vo.setStringIndex(bbu2.getBoardStringIndex());
        vo.setVendor(bbu2.getVendor().getLabel());
        vo.setRoomId(bbu2.getApparatusRoomId());
        vo.setBaseCounter(bbu2.getBaseBoardCounter());
        vo.setControlCounter(bbu2.getControlBoardCounter());
        vo.setPowerCounter(bbu2.getPowerBoardCounter());
        vo.setFrameCounter(bbu2.getFrameCounter());
        vo.setFunCounter(bbu2.getFunCounter());
        return vo;
    }

    /**
     * 锚点机房自我整合
     * @param roomBo
     */
    public void integrateWithInAnchorRoom(ApparatusRoomBo roomBo){
        List<Bbu2> bbu2List=roomBo.getBbuList();
        List<Bbu2> targetLIst=new ArrayList<>();
        Map<String,List<Bbu2>> sourceMapOfVendorAndGeneration=filterBbuByVendorAndNetGenerationOfBbuList(bbu2List);
        Map<String,List<Bbu2>> targetMapOfVendorAndGeneration=filterBbuByVendorAndNetGenerationOfBbuList(targetLIst);

        for (Map.Entry<String, List<Bbu2>> entry : targetMapOfVendorAndGeneration.entrySet()) {
            String vendorAndGeneration=entry.getKey();//hw_5g;
            List<Bbu2> targetBbuList=entry.getValue();
            List<Bbu2> sourceList=sourceMapOfVendorAndGeneration.get(vendorAndGeneration);

            List<Bbu2>[] sourceAndTarget= UtilBbuReturn.returnWhole(sourceList,targetBbuList,roomBo);

            UtilBbuReturn.integrateBoards2(sourceAndTarget[0],sourceAndTarget[1]);

        }
    }

    /**
     * 对 orderedRoomIdList 中包含的 roomId的锚点机房进行 整体搬迁和局部整合
     * 注意 整体搬迁  和  局部整合 的逻辑衔接
     * @param orderedRoomIdList
     * @param allRoomBoList
     * @param roomBoMap
     */
    public void returnWholeAndIntegrate(List<String> orderedRoomIdList,List<ApparatusRoomBo> allRoomBoList,Map<String, ApparatusRoomBo> roomBoMap){
        for (String roomId : orderedRoomIdList) {
            ApparatusRoomBo center=roomBoMap.get(roomId);
            if(!center.isAnchorRoom()){
                return;
            }

            List<ApparatusRoomBo> within5KmRoomBoList=cal5kmRoomOrderByDistance(allRoomBoList,center);
            int baseCounterWithIn5Km=UtilBbuReturn.getBaseBoardCounterOfApparatusRoomBoList(within5KmRoomBoList);
            int baseCounterOfCenterRoom=UtilBbuReturn.getBaseBoardCounter(center);
            List<Bbu2> centerBbuList=center.getBbuList();
            List<Bbu2> aroundBbuList=UtilBbuReturn.getBbuListFromApparatusRoomBoList(within5KmRoomBoList);


            //如果 锚点机房，能够安装最大的bbu数量，小于5公里内，需要整合的数量，那么按照距离远近，过滤掉一些待整合的bbu
            if(center.getBbuMaxSize()*center.getBaseSizePerBbu()<baseCounterWithIn5Km+baseCounterOfCenterRoom){
                aroundBbuList=UtilBbuReturn.filterSourceList(aroundBbuList,center);
            }

            //把 center中，baseCounter=0的bbu,从 targetList -> aroundBbuList
            //目的是：腾出来 机房bbu的位置,也是为了适应下面的 UtilReturn中的方法要求
            Iterator<Bbu2> iterator=centerBbuList.iterator();
            while (iterator.hasNext()){
                Bbu2 bbu2=iterator.next();
                if(bbu2.getBaseBoardCounter()==0){
                    aroundBbuList.add(0,bbu2);
                    iterator.remove();
                }
            }

            //获得厂家，网络4/5G boList map
            Map<String,List<Bbu2>> sourceMapOfVendorAndGeneration=filterBbuByVendorAndNetGenerationOfBbuList(aroundBbuList);
            Map<String,List<Bbu2>> targetMapOfVendorAndGeneration=filterBbuByVendorAndNetGenerationOfBbuList(centerBbuList);


            for (Map.Entry<String, List<Bbu2>> entry : targetMapOfVendorAndGeneration.entrySet()) {
                String vendorAndGeneration=entry.getKey();//hw_5g;
                List<Bbu2> targetBbuList=entry.getValue();
                List<Bbu2> sourceList=sourceMapOfVendorAndGeneration.get(vendorAndGeneration);

                // 先进行整体搬迁，给targetList 打好框架，即targetList就是锚点最终的bbu，只不过部分bbu的板卡位有空余
                List<Bbu2>[] sourceAndTarget=UtilBbuReturn.returnWhole(sourceList,targetBbuList,center);

                // 对targetList中，空余的插槽位置进行整合
                UtilBbuReturn.integrateBoards2(sourceAndTarget[0],sourceAndTarget[1]);
            }
        }
    }



    private Map<String, ApparatusRoomBo> filterApparatusRoomByApparatusRoomCategoryEnum(Map<String, ApparatusRoomBo> roomBoMap, ApparatusRoomCategoryEnum categoryEnum) {
        Map<String, ApparatusRoomBo> roomBoMapFiltered=new HashMap<>();
        for (Map.Entry<String, ApparatusRoomBo> entry : roomBoMap.entrySet()) {
            ApparatusRoomBo bo=entry.getValue();
            if(bo.getRoomCategory()==categoryEnum && bo.isAnchorRoom()){
                roomBoMapFiltered.put(entry.getKey(),bo);
            }
        }
        return roomBoMapFiltered;
    }


    /**
     * 构建 map ，形如  //<VendorEnum2.label_NetGenerationEnum.label,List<Bbu2>>
     *     //<zg_4g list<bbu>>
     * @param bbuList
     * @return
     */
    private Map<String,List<Bbu2>> filterBbuByVendorAndNetGenerationOfBbuList(List<Bbu2> bbuList){

        Map<String,List<Bbu2>> bbuMap=new HashMap<>();
        List<Bbu2> zx4GList=new ArrayList<>();
        List<Bbu2> zx5GList=new ArrayList<>();
        List<Bbu2> hw4GList=new ArrayList<>();
        List<Bbu2> hw5GList=new ArrayList<>();
        List<Bbu2> dt5GList=new ArrayList<>();
        String zx4G= VendorEnum2.ZHONG_XING.getLabel()+"_"+ NetGenerationEnum.G4.getLabel();
        String zx5G=VendorEnum2.ZHONG_XING.getLabel()+"_"+ NetGenerationEnum.G5.getLabel();
        String hw4G=VendorEnum2.HUA_WEI.getLabel()+"_"+ NetGenerationEnum.G4.getLabel();
        String hw5G=VendorEnum2.HUA_WEI.getLabel()+"_"+ NetGenerationEnum.G5.getLabel();
        String datG=VendorEnum2.DA_TANG.getLabel()+"_"+ NetGenerationEnum.G5.getLabel();
        bbuMap.put(zx4G,zx4GList);
        bbuMap.put(zx5G,zx5GList);
        bbuMap.put(hw4G,hw4GList);
        bbuMap.put(hw5G,hw5GList);
        bbuMap.put(datG,dt5GList);

        for (Bbu2 bbu2 : bbuList) {
            if(bbu2.getVendor()==VendorEnum2.HUA_WEI){
                if(bbu2.getNetGenerationFromBbuModel()==NetGenerationEnum.G4){
                    hw4GList.add(bbu2);
                }else{
                    hw5GList.add(bbu2);
                }
            }
            if(bbu2.getVendor()==VendorEnum2.ZHONG_XING){
                if(bbu2.getNetGenerationFromBbuModel()==NetGenerationEnum.G4){
                    zx4GList.add(bbu2);
                }else{
                    zx5GList.add(bbu2);
                }
            }
            if(bbu2.getVendor()==VendorEnum2.DA_TANG){
                dt5GList.add(bbu2);
            }
        }
        return bbuMap;
    }

    /**
     * 对 roomMap ，根据经纬度顺序，进行排序，确保每次room的顺序一致，
     * 以保证 相同输入 获得 同样的 输出
     * @param boMap
     * @return
     */
    public List<String> orderRoomBoByLonAndLat(Map<String,ApparatusRoomBo> boMap){
        Collection<ApparatusRoomBo> collection=boMap.values();
        List<ApparatusRoomBo> boList=new ArrayList<>(collection);

        boList.sort(new Comparator<ApparatusRoomBo>() {
            @Override
            public int compare(ApparatusRoomBo o1, ApparatusRoomBo o2) {
                if(Math.abs(o1.getLatitude()-o2.getLatitude())>0.0001){
                    return (int) ((o1.getLatitude()-o2.getLatitude())*10000);
                }else{
                    return (int) ((o1.getLongitude()-o2.getLongitude())*10000);
                }
            }
        });
        List<Long> idList=boList.stream().map(ApparatusRoomBo::getId).toList();
        List<String> idStringList=new ArrayList<>();
        for (Long aLong : idList) {
            idStringList.add(aLong+"");
        }


        return idStringList;
    }

    /**
     * 不包含center,不包含锚点机房
     * @param roomList
     * @param centerRoom
     * @return
     */
    public  List<ApparatusRoomBo> cal5kmRoomOrderByDistance(List<ApparatusRoomBo> roomList,ApparatusRoomBo centerRoom){
        List<ApparatusRoomBo> within5kmRoomList=new ArrayList<>();
        for (ApparatusRoomBo bo : roomList) {
            if(bo==centerRoom || bo.isAnchorRoom()){
                continue;
            }
            double distance=Math.abs(NslUtilGis.getDistance(centerRoom.getLatitude(),centerRoom.getLongitude(),bo.getLatitude(),bo.getLongitude()));
            if(distance<=5000){
                bo.setTempDistance(distance);
                within5kmRoomList.add(bo);
            }
        }
        within5kmRoomList.sort(Comparator.comparingDouble(ApparatusRoomBo::getTempDistance));


        return within5kmRoomList;
    }
}
